package cn.duyo.spring.aop.framework.autoproxy;

import cn.duyo.spring.aop.*;
import cn.duyo.spring.aop.aspectj.AspectJExpressionPointcutAdvisor;
import cn.duyo.spring.aop.framework.ProxyFactory;
import cn.duyo.spring.beans.BeansException;
import cn.duyo.spring.beans.PropertyValues;
import cn.duyo.spring.beans.factory.BeanFactory;
import cn.duyo.spring.beans.factory.BeanFactoryAware;
import cn.duyo.spring.beans.factory.config.InstantiationAwareBeanPostProcessor;
import cn.duyo.spring.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * 融入Bean生命周期的自动代理创建者 ！！！AOP核心
 *
 * 但是要避免对AOP基础设施类本身进行代理，如果Advisor、Advice、Pointcut等组件被代理，可能导致代理嵌套或AOP配置逻辑混乱（防止重复代理或循环依赖）。
 *
 * @author du
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    // TODO 解决循环依赖
    private final Set<Object> earlyProxyReferences = Collections.synchronizedSet(new HashSet<>());

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }


    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {

        /**
         * TODO 之前是放到Before中执行，但导致并未在 bean 的生命周期，现在把AOP代理对象创建的逻辑移动到 After
         */
//        if (isInfrastructureClass(beanClass)) return null;
//
//        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
//
//        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
//            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
//            if (!classFilter.matches(beanClass)) continue;
//
//            AdvisedSupport advisedSupport = new AdvisedSupport();
//
//            TargetSource targetSource = null;
//            try {
//                // 此处有BUG
//                targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//            advisedSupport.setTargetSource(targetSource);
//            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
//            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
//            advisedSupport.setProxyTargetClass(false);
//
//            return new ProxyFactory(advisedSupport).getProxy();
//        }
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    private boolean isInfrastructureClass(Class<?> beanClass) {
        /**
         * 排除基础设施类（如 Advice、Pointcut、Advisor 自身）
         * 避免对AOP基础设施类本身进行代理, 防止重复代理或循环依赖
         * 1. 判断类是否是Advice的子类或实现类（增强的具体逻辑，如@Before、@After等注解对应的通知逻辑）
         * 2. 判断类是否是Pointcut的子类或实现类（主要是对 aspectj 包提供的表达式校验方法使用。用于定义切入点，确定哪些方法需要被拦截）
         * 3. 判断类是否是Advisor的子类或实现类（组合了Advice和Pointcut，提供完整的切面配置）
         */
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO *********** 此处后置处理器会后执行 *************
        //System.out.println("开始：AOP后置处理器开始创建代理对象，此时代理对象是否存在：" + earlyProxyReferences.contains(beanName) + " beanName=" + beanName);
        if (!earlyProxyReferences.contains(beanName)) {
            //System.out.println("开始：AOP后置处理器开始创建代理对象，还未创建则进行尝试创建，beanName=" + beanName);
            return wrapIfNecessary(bean, beanName);
        }
        return bean;
    }

    protected Object wrapIfNecessary(Object bean, String beanName) {
        if (isInfrastructureClass(bean.getClass())) return bean;

        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            // 过滤匹配类
            if (!classFilter.matches(bean.getClass())) continue;

            System.out.println("开始：AOP后置处理器开始创建代理对象，PointCut命中，开始真正创建代理对象，beanName=" + beanName);

            AdvisedSupport advisedSupport = new AdvisedSupport();

            TargetSource targetSource = new TargetSource(bean);
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(true); // TODO true为强制使用Cglib

            /**
             * 返回代理对象
             * TODO 源码中还会有 proxyFactory.copyFrom(this); // 继承全局配置（如优化选项、代理目标类设置如：是否暴露代理对象-exposeProxy，方法过滤规则-preFiltered）
             *   getProxyClassLoader等
             */
            return new ProxyFactory(advisedSupport).getProxy(/*getProxyClassLoader()*/);
        }
        return bean;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        // TODO *********** 此处会在 getSingleton(beanName) 时触发执行，只会在有循环依赖时才会触发执行，先于后置处理器执行 *************
        System.out.println("代理类AOP开始缓存，beanName=" + beanName);
        earlyProxyReferences.add(beanName);
        return wrapIfNecessary(bean, beanName);
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return pvs;
    }

    // 源代码：获取匹配的 Advisor
    /*protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }*/

}
